import { Command, SpawnOptions } from '@tauri-apps/api/shell';
import { app, fs, invoke, path } from '@tauri-apps/api';
import { parse } from 'yaml';

type SpawnType = {
  program: string;
  args: string | string[];
  options?: SpawnOptions;
  callback?: (args: any) => any;
};

export const spawn = async ({
  program,
  args,
  options,
  callback,
}: SpawnType) => {
  const stdout: string[] = [];
  const stderr: string[] = [];

  console.log(program, args);
  const child = new Command(program, args, options);
  child.stdout.on('data', (data) => {
    callback?.(data);
    stdout.push(data);
    console.log('stdout', data);
  });
  child.stderr.on('data', (data) => {
    callback?.(data);
    stderr.push(data);
    console.log('stderr', data);
  });

  child.spawn().catch((err) => {
    console.log(err);
  });
  return new Promise((resolve, reject) => {
    child.on('error', (error) => {
      console.log('error', error);
    });
    child.on('close', (data) => {
      console.log('close', data);
      if (data.code === 0) {
        resolve(stdout);
      } else {
        const err = { ...data };
        err.stderr = stderr;
        err.stdout = stdout;
        reject(err);
      }
    });
  });
};

export async function getPythonDir() {
  const settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
  if (settings?.python?.length > 0) {
    return settings?.python;
  }
  return await path.join(await path.resourceDir(), 'vendor', 'python');
}

export async function python({ program, args, options, callback }: SpawnType) {
  const py = await path.join(await getPythonDir(), `python.exe`);
  return await spawn({
    program: 'python',
    args: ['-m', program, ...args],
    options: {
      ...options,
      env: {
        PYTHONIOENCODING: 'utf-8',
        ...(options?.env ?? {}),
      },
    },
    callback,
  });
}
export async function pyVersion() {
  const py = await path.join(await getPythonDir(), `python.exe`);
  return await spawn({
    program: py,
    args: ['--version'],
  });
}

export async function pipInstall(
  name: string,
  callback: SpawnType['callback'],
) {
  await python({
    program: 'pip',
    args: ['install', name, '-U'],
    callback,
  });
  return await python({
    program: `pip`,
    args: ['install', name, '-U', '--force', '--no-deps'],
    callback,
  });
}

export async function pipConfig() {
  const options = {
    'index-url': 'http://mirrors.tools.huawei.com/pypi/simple/',
    'trusted-host': 'mirrors.tools.huawei.com',
  };
  for await (let [key, value] of Object.entries(options)) {
    const res = await spawn({
      program: 'pip',
      args: ['config', 'set', `global.${key}`, value],
    });
    console.log(res);
  }
}

export async function getenv(name: string) {
  return await invoke('getenv', {
    name,
  });
}

export async function getPackages() {
  const appsDir = await path.join(await path.resourceDir(), `vendor`, `apps`);
  const paths = await fs.readDir(appsDir);
  const obj: any = {};
  for (const item of paths) {
    try {
      const pkg = await path.join(item.path, 'package.json');
      const text = await fs.readTextFile(pkg);
      const app = JSON.parse(text);
      obj[app.name] = { ...app, path: item.path };
    } catch (error) {}
  }
  return obj;
}

export async function getApps() {
  const appsDir = await path.join(
    await path.resourceDir(),
    `vendor`,
    `apps`,
    `apps.yaml`,
  );
  const text = await fs.readTextFile(appsDir);
  return parse(text);
}

export async function getJson() {
  return await invoke('get_json');
}
export async function exists(path: string) {
  return await invoke('exists', { path });
}

export function toArgs(values: any) {
  return Object.entries(values)
    .map(([key, val]) => [`--${key}`, `${val}`])
    .flat();
}

export async function getApp(appDir: string) {
  try {
    const pkg = await path.join(appDir, 'manifest.json');
    if (!(await exists(pkg))) {
      return;
    }
    const text = await fs.readTextFile(pkg);
    return JSON.parse(text);
  } catch (error) {
    return;
  }
}
export async function getDevApps(appDir: string): Promise<any[]> {
  try {
    const pkg = await path.join(appDir, 'manifest.json');
    if (!(await exists(pkg))) {
      const paths = await fs.readDir(appDir);
      return paths.map(async ({ path }) => await getDevApps(path)).flat();
    }
    const obj: any = await invoke('get_manifest', { path: appDir });
    // const obj = JSON.parse(text);
    return [
      {
        ...obj,
        main: (await path.join(appDir, obj.main)),
      },
    ];
  } catch (error) {
    return [];
  }
}
// export async function getDevApps(appDir: string): Promise<any[]> {
//   try {
//     const pkg = await path.join(appDir, 'manifest.json');
//     if (!(await exists(pkg))) {
//       const paths = await fs.readDir(appDir);
//       return paths.map(async ({ path }) => await getDevApps(path)).flat();
//     }
//     const text = await fs.readTextFile(pkg);
//     const obj = JSON.parse(text);
//     return [
//       {
//         ...app,
//         main: path.join(appDir, app.main).path,
//       },
//     ];
//   } catch (error) {
//     return [];
//   }
// }
